import { clone } from "radash";

/**
 * 深度合并对象
 * @param target
 * @param objects
 */
export function deepMerge<T extends object>(target: Partial<T>, ...objects: (DeepPartial<T> | undefined | null | false)[]): T {
  objects.forEach(obj => {
    if (!obj) {
      return;
    }
    (Object.keys(obj) as (keyof T)[]).forEach(key => {
      (target as any)[key] =
        (target[key] as any)?.toString() === "[object Object]" && (obj[key] as any)?.toString() === "[object Object]"
          ? deepMerge((target as any)[key], (obj as any)[key])
          : ((target as any)[key] = obj[key]);
    });
  });
  return target as T;
}

/**
 * 合并两个对象数组,将数组2的元素添加到数组1中,并返回新的数组
 * 数组2中元素不在数组1中,追加合并到数组1中,存在的合并对象属性
 * @param arr1 数组1
 * @param arr2 数组2
 * @param predicate 判断数组2中的元素是否不存在
 * @param mergeOld 如果合并对象已存在,使用旧对象合并新对象
 */
export function mergeObjects<T extends object>(
  arr1: T[],
  arr2: T[],
  predicate: (item1: T, item2: T, rindex: number, index2: number, result: T[], arr2: T[]) => unknown,
  mergeOld = true,
): T[] {
  const retArr = arr1.slice();
  for (let i = 0; i < arr2.length; i++) {
    const it2 = arr2[i];
    const index = retArr.findIndex((it, ri) => predicate(it, it2, ri, i, retArr, arr2));
    if (index === -1) {
      retArr.push(it2);
    } else if (mergeOld) {
      retArr[index] = { ...retArr[index], ...it2 };
    }
  }
  return retArr;
}

/**
 * 根据属性合并两个对象数组
 * @param arr1 数组1
 * @param arr2 数组2
 * @param prop 对象属性名
 * @param mergeOld 如果合并对象已存在,使用旧对象合并新对象
 * @see mergeObjects
 */
export function mergeObjectsByProp<T extends object>(arr1: T[], arr2: T[], prop: keyof T, mergeOld = true): T[] {
  return mergeObjects(arr1, arr2, (item1, item2) => item1[prop] === item2[prop], mergeOld);
}

/**
 * 深度合并对象，返回一个新的对象，而不是第一个参数
 * @param target
 * @param objects
 */
export function deepMergeN<T extends object>(target: Partial<T>, ...objects: (DeepPartial<T> | undefined | null | false)[]): T {
  objects.forEach(obj => {
    if (!obj) {
      return;
    }
    (Object.keys(obj) as (keyof T)[]).forEach(key => {
      (target as any)[key] =
        (target[key] as any)?.toString() === "[object Object]" && (obj[key] as any)?.toString() === "[object Object]"
          ? deepMergeN((target as any)[key], (obj as any)[key])
          : ((target as any)[key] = obj[key]);
    });
  });
  return { ...target } as T;
}

/**
 * 判断对象为空
 *  - 值为为null/undefined
 *  - 对象的所有属性值为null/undefined
 *  - 数组长度为0
 *  - Set/Map size=0
 *
 * @param o object
 */
export function isBlankObject<T>(o: T | null | undefined): o is null | undefined {
  if (o == null) {
    return true;
  }

  if (Array.isArray(o)) {
    return o.filter(it => it != null).length === 0;
  }
  if (o instanceof Set || o instanceof Map) {
    return o.size === 0;
  }
  if (typeof o === "object") {
    return Object.keys(o).filter(k => o[k as unknown as keyof T] != null).length === 0;
  }

  return false;
}

/**
 * 判断对象不为空
 *  - 值不为null/undefined
 *  - 对象的有属性值不为null/undefined
 *  - 数组长度 > 0
 *  - Set/Map size > 0
 * @param o
 */
export function isNotBlankObject<T>(o: T | null | undefined): o is T {
  return !isBlankObject(o);
}

export function assign<T>(o: false | null | undefined | T, ...objs: (false | null | undefined | Partial<T>)[]): T {
  return Object.assign(o || Object.create(null), ...objs.filter(it => !!it));
}

export function deepEqual(o1: any, o2: any) {
  if (o1 === o2) {
    return true;
  }
  if (!(typeof o1 == "object" && o1 != null) || !(typeof o2 == "object" && o2 != null)) {
    return false;
  }
  //比较对象内部
  if (Object.keys(o1).length != Object.keys(o2).length) {
    return false;
  }
  for (const prop in o1) {
    if (o2.hasOwnProperty(prop)) {
      if (!deepEqual(o1[prop], o2[prop])) {
        return false;
      }
    } else {
      return false;
    }
  }
  return true;
}

export function shallowEqual(o1: any, o2: any) {
  if (o1 === o2) {
    return true;
  }
  if (!(typeof o1 === "object" && o1 != null) || !(typeof o2 === "object" && o2 != null)) {
    return false;
  }
  const keysA = Object.keys(o1);
  const keysB = Object.keys(o2);
  if (keysA.length !== keysB.length) {
    return false;
  }
  for (let i = 0; i < keysA.length; i++) {
    if (o2.hasOwnProperty(keysA[i])) {
      if (o1[keysA[i]] !== o2[keysA[i]]) {
        return false;
      }
    } else {
      return false;
    }
  }
  return true;
}

export function copyProps<S extends object, R extends object>(src: S, dest: R) {
  const entries = Object.entries(src);
  entries.forEach(([prop, value]) => ((dest as Dict)[prop] = value));
}

export function setProp<T, K extends keyof T>(obj: T, prop: K | string, value: T[K]): T;
export function setProp(obj: any, prop: string, value: any): any;
export function setProp<T, K extends keyof T>(obj: T, prop: K | string, value: any): T {
  (obj as any)[prop] = value;
  return obj;
}

/**
 * (浅)复制对象
 * @param object
 * @param omits 要删除的属性
 */
export function copyObject<S extends object, O extends keyof S>(object: S, omits?: O[]): Omit<S, O> {
  const copy = clone(object);
  if (omits && omits.length) {
    for (const omit of omits) {
      delete copy[omit];
    }
  }
  return copy;
}

export function parseJson<T>(json: Nil<string>): T | null {
  if (json == null) {
    return null;
  }
  return JSON.parse(json) as T;
}

/**
 * 删除对象中值为null/undefined的属性,返回新的对象
 * @param o
 */
export function omitNil<T extends {}>(o: T): T {
  const keys = Object.keys(o);
  return keys.reduce((ret, key) => {
    const value = (o as any)[key];
    if (value != null) {
      (ret as any)[key] = value;
    }
    return ret;
  }, {} as T);
}

export const defineObj = <T>(o: T): T => o;

export function clearObject(obj: any) {
  if (obj != null) {
    if (obj instanceof Map || obj instanceof Set) {
      obj.clear();
    } else if (Array.isArray(obj)) {
      obj.length = 0;
    } else if (typeof obj === "object") {
      Object.keys(obj).forEach(key => delete obj[key]);
    }
  }
}
